
#include "singlestep.h"
//#include "pixelhistory.h"
#include "rulebase.h"
#include <assert.h>

void SingleStep::updateFromRuleBase(const RuleBase& rb, Rectangle2FormationAlgorithmType algorithmType)
{
	if (this->ruletangle.size() != 0)
	{
		cerr << "Warning: This SingleStep object already has some RuleTngle objects. They will be erased." << endl;
		this->ruletangle.clear();
	}
	//this->phaseNum = rb.getInstanceSize();
	this->phaseNum = rb.getNBsperOneLongInstance() + 1;
	bool* flag = new bool[rb.getInstanceNum()]; //this is a flag to tell which rules have been used for creating the ruletangles.
	for (int i = 0; i < rb.getInstanceNum(); i++) flag[i] = false;
	int conflictIndex;

	for (int i = 0; i < (int) rb.getInstanceNum(); i++)
	{
		if (flag[i] != true)
		{
			flag[i] = true;
			conflictIndex = rb.indexOfConflicting(rb.getInstance(i));
			if (conflictIndex == -1) //This rule has no conflicting rule
			{
				//TODO: We might want to add it only if the label of the longinstance is black (1)
#ifdef SAVE_BLACKS_ONLY
				if (rb.getInstance(i).getLabel() == BLACK)
#endif
					this->ruletangle.push_back(Ruletangle2(rb.getInstance(i)));
			}
			else //There is a conflicting rule in the rulebase
			{
				flag[conflictIndex] = true;
				//TODO: Add the type to tell the major longinstance.
				Ruletangle2 toBeAdded(rb.getInstance(i), rb.getInstance(conflictIndex), algorithmType);
				if (toBeAdded.getMajorLongInstance().getLabel() == -1) //error Happened
				{
					this->ruletangle.clear();
					this->phaseNum = -1;
					return;
				}
				this->ruletangle.push_back(toBeAdded);
			}
		}
	}
	delete[] flag;
}

SingleStep::SingleStep (const EightLayerIM& lImage, Rectangle2FormationAlgorithmType algorithmType)
{
	RuleBase rb(lImage);
	this->updateFromRuleBase(rb, algorithmType);
};

SingleStep::SingleStep (const EightLayerIM& lImage, int stepsNum, Rectangle2FormationAlgorithmType algorithmType) : phaseNum(stepsNum)
{
	//const int eight = 8;
	//ImageMatrix tempImages[eight];
	//lImage.copyImagesTo(tempImages, eight);
	//RuleBase rb(tempImages, stepsNum); //rb might have conflicting rules but no duplicate of identical rules.
	RuleBase rb(lImage, 0, stepsNum+1);
	this->updateFromRuleBase(rb, algorithmType);
};

SingleStep::SingleStep (const EightLayerIM& lImage, int startI, int endI, Rectangle2FormationAlgorithmType algorithmType) : phaseNum(endI-startI)
{
	RuleBase rb(lImage, startI, endI); //rb might have conflicting rules but no duplicate of identical rules.
#ifdef COMMENTS_ON
	cout << "Creating SingleStep from the following RuleBase:" << endl;
	rb.print();
#endif
	this->updateFromRuleBase(rb, algorithmType);
}

SingleStep::SingleStep (string baseName, int layerNum, string type, Rectangle2FormationAlgorithmType algorithmType): phaseNum(layerNum)
{
	//const int eight = 8;
	//ImageMatrix tempImages[eight];
	EightLayerIM lImage(baseName, layerNum, type);
	//lImage.copyImagesTo(tempImages, eight);
	RuleBase rb(lImage); //rb might have conflicting rules but no duplicate of identical rules.
	bool* flag = new bool[rb.getInstanceNum()]; //this is a flag to tell which rules have been used for creating the ruletangles.
	for (int i = 0; i < rb.getInstanceNum(); i++) flag[i] = false;
	int conflictIndex;
	
	for (int i = 0; i < (int) rb.getInstanceNum(); i++)
	{
		if (flag[i] != true)
		{
			flag[i] = true;
			conflictIndex = rb.indexOfConflicting(rb.getInstance(i));
			if (conflictIndex == -1) //This rule has no conflicting rule
				//TODO: We might want to add only the longinstances whose outputs are black (1)
				this->ruletangle.push_back(Ruletangle2(rb.getInstance(i)));
			else //There is a conflicting rule in the rulebase
			{
				flag[conflictIndex] = true;
				//TODO: add the type to tell the major longinstance
				this->ruletangle.push_back(Ruletangle2(rb.getInstance(i), rb.getInstance(conflictIndex), algorithmType));
			}
		}
	}
	delete[] flag;
}


int SingleStep::nextState (const vector<Neighborhood*>& nb, const Coordinate& coo) const
{
	for (vector<Ruletangle2>::const_iterator it = this->ruletangle.begin(); it != this->ruletangle.end(); it++)
		if (it->applies(nb))
		{
			//if (it->hasCooAsMinor(coo))
			//	return it->label();
			//else
			//	return 1;
			//TODO: we will return black (1) if we are only keeping the ruletangles whose outputs are 1.
			return it->label(nb, coo);
		}
	//only the rules with output = 1 are listed. the out put for the given neighborhood/coo is white if nothing is stored.
	return 0;

}

void SingleStep::apply (const EightLayerIM& source, ImageMatrix& target) const
{
	assert (source.getValidImages() == this->phaseNum && target.getRowNum() == source.getRowNum() && target.getColNum() == source.getColNum() 
		&& "Error: Creating the next phase of a sequence of images whose number is not the same as the valid bits in SingleStep");
	//bool newStateFound;
	vector<Neighborhood*> history;
	ImageMatrix currentImage;
	int nextState;
	for (int row = 0; row < target.getRowNum(); row++)
		for (int col = 0; col < target.getColNum(); col++)	//start finding the next states of the pixels, one by one
		{
			for (int i = 0 ; i < source.getValidImages(); i++) //create the neighborhood history of the current pixel
			{
				currentImage = source.getImage(i);
				history.push_back(new Neighborhood(currentImage, row, col));
			}
			nextState = this->nextState(history, Coordinate(row, col));
			if (nextState == -1)
				cout << "Warning: This singlestep has no rule for one of the neighborhood histories.";
			target.setData_noClusterUpdate(row, col, nextState);
		}

		for (vector<Neighborhood*>::const_iterator i = history.begin(); i != history.end(); ++i)
			delete *i;
		history.clear();
}

void SingleStep::apply (EightLayerIM& lImage) const
{
	assert (lImage.getValidImages() < 8  
		&& "Error: the layered image is full,, no next state can be created.");
	assert (lImage.getValidImages() == this->phaseNum - 1 && "The imput EightLayerIM has not the same number"
		&& " of valid images as the IF part of the SingleStep object.");
	//bool newStateFound;
	vector<Neighborhood*> history;
	ImageMatrix currentImage;
	ImageMatrix target;
	int nextState;
	for (int row = 0; row < lImage.getRowNum(); row++)
		for (int col = 0; col < lImage.getColNum(); col++)	//start finding the next states of the pixels, one by one
		{
			history.clear();
			for (int i = 0 ; i < lImage.getValidImages(); i++) //create the neighborhood history of the current pixel
			{
				currentImage = lImage.getImage(i);
				history.push_back(new Neighborhood(currentImage, row, col));
			}
			nextState = this->nextState(history, Coordinate(row, col));
			if (nextState == -1)
				cout << "Warning: This singlestep has no rule for one of the neighborhood histories.";
			target.setData_noClusterUpdate(row, col, nextState);
		}
		lImage.addImageMatrix(target);
		for (vector<Neighborhood*>::const_iterator i = history.begin(); i != history.end(); ++i)
			delete *i;
		history.clear();
}

void SingleStep::print(void)
{
	cout << "Number of phases: " << this->phaseNum << endl;
	cout << "Number of RuleTangle objects in the SingleStep: " << ruletangle.size();
	for (int i = 0; i < (int) this->ruletangle.size(); i++)
	{
		cout << endl << "Printing rule " << i << ":";
		this->ruletangle[i].print();
	}
}

ostream& operator<< (ostream& stream, const SingleStep& ss)
{
	stream << "The SingleStep object:\n";
	for (int i = 0; i < (int) ss.ruletangle.size(); i++)
	{
		stream << ss.ruletangle[i];
	}
	stream << "Number of phases: " << ss.phaseNum << endl;
	stream << "Number of rules: "  << ss.getRuletangleNum() <<endl;
	stream << "Info Size in bits: "<< ss.getNetInfoSizeInBits() << endl;
	stream << "Number of RuleTangle objects in the SingleStep: " << ss.ruletangle.size() << endl;
	return stream;
}

//void SingleStep::apply (const ImageMatrix& source, ImageMatrix& target) const
//{
//}
//
//void SingleStep::addNewPhase (const ImageMatrix& nextPhase)
//{
//}

int SingleStep::getNetInfoSizeInBits(void) const
{
	int result = 0;
	for (int i = 0; i < (int) this->ruletangle.size(); i++)
		result += this->ruletangle[i].getNetInfoSizeInBits();
	return result;
}

void SingleStep::apply (EightLayerIM& lImage, int firstLayer, int lastLayer) const
{
	assert (lImage.getValidImages() < 8  && firstLayer >= 0 && firstLayer <= lastLayer && lastLayer < 7
		&& "Error: the layered image is full,, no next state can be created.");
	assert (lastLayer - firstLayer + 1 == this->phaseNum - 1 && "The imput EightLayerIM has not the same number"
		&& " of valid images as the IF part of the SingleStep object.");
	//bool newStateFound;
	if (lImage.getValidImages() > lastLayer)
		cout << "Warning: The input EightLayerIM has more valid images than what will be used in SingleStep::apply()."
		<< "The layers after the given lastLayer (" << lastLayer << ") will be deleted." << endl;
	lImage.setvalidImages(lastLayer+1);
	vector<Neighborhood*> history;
	ImageMatrix currentImage;
	ImageMatrix target;
	int nextState;
	for (int row = 0; row < lImage.getRowNum(); row++)
		for (int col = 0; col < lImage.getColNum(); col++)	//start finding the next states of the pixels, one by one
		{
			history.clear();
			for (int i = firstLayer ; i <= lastLayer; i++) //create the neighborhood history of the current pixel
			{
				currentImage = lImage.getImage(i);
				history.push_back(new Neighborhood(currentImage, row, col));
			}
			nextState = this->nextState(history, Coordinate(row, col));
			if (nextState == -1)
				cout << "Warning: This singlestep has no rule for one of the neighborhood histories.";
			target.setData_noClusterUpdate(row, col, nextState);
		}
		lImage.addImageMatrix(target);
		for (vector<Neighborhood*>::const_iterator i = history.begin(); i != history.end(); ++i)
			delete *i;
		history.clear();
}